//////////////////////////////////////////////////////////////////////////////////
//	 Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include "configdata.h"
#include "scriptgenerator.h"
#include "sysdepapi.h"
#include "../common/MarkupSTL.h"




static CScriptGenerator::SRCVAL valNATOPT[] =
{
	{ wxT("ip_conntrack_generic_timeout"),			wxT("600")		},
	{ wxT("ip_conntrack_icmp_timeout"),				wxT("10")		},
	{ wxT("ip_conntrack_tcp_timeout_close"),		wxT("5")		},
	{ wxT("ip_conntrack_tcp_timeout_close_wait"),	wxT("10")		},
	{ wxT("ip_conntrack_tcp_timeout_fin_wait"),		wxT("15")		}, /*==tcp_fin_timeout*/
	{ wxT("ip_conntrack_tcp_timeout_time_wait"),	wxT("60")		},
	{ wxT("ip_conntrack_udp_timeout"),				wxT("60")		},
	{ wxT("ip_conntrack_tcp_be_liberal"),			wxT("1")		},
	{ wxT("ip_conntrack_tcp_timeout_syn_sent"),		wxT("120")		},
};

static CScriptGenerator::SRCVAL valTCPOPT[] =
{
	{ wxT("tcp_fin_timeout"),						wxT("15")		},
	{ wxT("tcp_keepalive_time"),					wxT("1800")		},/*30 min*/
	{ wxT("tcp_keepalive_intvl"),					wxT("180")		},
	{ wxT("tcp_keepalive_probes"),					wxT("2")		},
	{ wxT("tcp_orphan_retries"),					wxT("1")		},
	{ wxT("tcp_syncookies"),						wxT("1")		},
	{ wxT("tcp_synack_retries"),					wxT("4")		},
	{ wxT("tcp_syn_retries"),						wxT("4")		},
	{ wxT("tcp_window_scaling"),					wxT("1")		},
	{ wxT("tcp_tw_reuse"),							wxT("1")		},
	{ wxT("tcp_sack "),								wxT("1")		},
	{ wxT("tcp_timestamps "),						wxT("0")		},
};

static CScriptGenerator::SRCVAL valP2P[] =
{
	{ wxT("ip_conntrack_tcp_timeout_established"),	wxT("1800")		}, /*30 min*/
	{ wxT("ip_conntrack_generic_timeout"),			wxT("300")		},
	{ wxT("ip_conntrack_udp_timeout"),				wxT("30")		},
	{ wxT("ip_conntrack_udp_timeout_stream"),		wxT("120")		},
	{ wxT("ip_conntrack_tcp_timeout_close"),		wxT("5")		},
	{ wxT("ip_conntrack_tcp_timeout_close_wait"),	wxT("10")		},
	{ wxT("ip_conntrack_tcp_timeout_fin_wait"),		wxT("15")		}, /*==tcp_fin_timeout*/
	{ wxT("ip_conntrack_tcp_timeout_time_wait"),	wxT("60")		},
	{ wxT("ip_conntrack_tcp_timeout_syn_sent"),		wxT("120")		},
};

static CScriptGenerator::SRCVAL valWWW[] =
{
	{ wxT("ip_conntrack_tcp_timeout_established"),	wxT("432000")	}, /*5 days*/
};

static CScriptGenerator::SRCVAL valICMP[] =
{
	{ wxT("icmp_echo_ignore_broadcasts"),			wxT("1")		},
	{ wxT("icmp_ignore_bogus_error_responses"),		wxT("1")		},
};



CScriptGenerator::CScriptGenerator( CConfigData& data ):m_bXML(false)
{
	m_strMACBlockChainName = wxT("rtrcmd");
	m_strNatChainName = wxT("rtrcmdnat");
	m_strPortForwardChainName = wxT("rtrcmdpf");

	wxString strSeed;

	try
	{
		strSeed = data.GetUniqueSeed();
	}
	catch(...) {}

	try
	{
		if ( strSeed.empty() )
		{
			MACLIST	listInt = CSysDepAPI::GetPCInterfacesMACList();

			if ( !listInt.empty() )
			{
				MACENTRY entry = listInt[0];
				strSeed = wxString::FromAscii( entry.strMAC.c_str() );

				if ( !strSeed.empty() )
				{
					strSeed = strSeed.Right(2); 
					data.SetUniqueSeed(strSeed);
				}
			}
		}
	}
	catch(...) {}

	m_strNatChainName+=strSeed;
	m_strPortForwardChainName+=strSeed;
	m_strMACBlockChainName+=strSeed;
}

CScriptGenerator::~CScriptGenerator(void)
{
}

wxString CScriptGenerator::GenerateScript( SRCVAL vals[], size_t tSize )
{
	wxString str;

	CMarkupSTL	doc;
	doc.AddElem( XML_SCRIPT_EL );
	doc.AddAttrib( GLOBAL_TYPE_ATR, SHELL_TCPNAT_SETTNGS );

	for ( size_t i=0;i<tSize;i++ )
	{
		doc.AddChildElem(GLOBAL_ENTRY_EL);
		doc.AddChildAttrib(GLOBAL_NAME_ATR,  vals[i].file.ToAscii());
		doc.AddChildAttrib(GLOBAL_VALUE_ATR, vals[i].val.ToAscii());

		str+=wxT("echo \"");
		str+=vals[i].val;
		str+=wxT("\">");
		str+=DEF_NETFILTER_PATH;
		str+=vals[i].file;
		str+=_(TELNET_ENTER);
	}

	return m_bXML?wxString::FromAscii( doc.GetDoc().c_str() ): str;
}

wxString CScriptGenerator::GenerateNATOptScript()
{
	return GenerateScript(valNATOPT, sizeof(valNATOPT)/sizeof(valNATOPT[0]));
}

wxString CScriptGenerator::GenerateTCPScript()
{
	return GenerateScript(valTCPOPT, sizeof(valTCPOPT)/sizeof(valTCPOPT[0]));
}

wxString CScriptGenerator::GenerateP2PScript()
{
	return GenerateScript(valP2P, sizeof(valP2P)/sizeof(valP2P[0]));
}

wxString	CScriptGenerator::GenerateWWWScript()
{
	return GenerateScript(valWWW, sizeof(valWWW)/sizeof(valWWW[0]));
}

wxString CScriptGenerator::GenerateICMPScript()
{
	return GenerateScript(valICMP, sizeof(valICMP)/sizeof(valICMP[0]));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//iptables -L --line-numbers -n -v
//INPUT = to router local
//FORWARD = transit
wxString CScriptGenerator::GenerateMACBlockScript(const MACBLOCK& lstWhite,
												  const MACBLOCK& lstBlack,
												  bool bLog)
{
	wxString str;

	if ( m_bXML )
	{
		return GenerateMACBlockScriptXML(lstWhite, lstBlack, bLog);
	}

	//add new chain
	str+=wxT("iptables -t nat -N routercmd"); str+=wxT(TELNET_ENTER); 

	//clear chain
	str+=wxT("iptables -t nat -F routercmd"); str+=wxT(TELNET_ENTER); 

	//add chain to PREROUTING
	str+=wxT("iptables -t nat -D PREROUTING -j routercmd");str+=wxT(TELNET_ENTER);
	//str+=wxT("iptables -t nat -I PREROUTING 2 -j routercmd");str+=wxT(TELNET_ENTER);
	//str+=wxT("iptables -t nat -I PREROUTING 1 -j routercmd");str+=wxT(TELNET_ENTER);
	str+=wxT("iptables -t nat -A PREROUTING  -j routercmd");str+=wxT(TELNET_ENTER);

	//process white list
	for(size_t i=0;i<lstWhite.list.size();i++)
	{
		wxString strMAC = wxString::FromAscii( lstWhite.list[i].strMAC.c_str() );
		str+=wxT("iptables -t nat -A routercmd -m mac --mac-source ");
		str+=strMAC;

		if ( lstWhite.list[i].bEnableSched &&
			 lstWhite.list[i].IsBlockedByScheduler() )
		{
			str+=wxT(" -j DROP");
		}
		else
		{
			//was
			//str+=wxT(" -j ACCEPT");
			str+=wxT(" -j RETURN");
		}

		str+=wxT(TELNET_ENTER);
	}

	if ( lstWhite.blockOthers )
	{
		//Drop all others
		if ( bLog )
		{
			str+=wxT("iptables -t nat -A routercmd  -j LOG --log-prefix \"MAC blocked\""); str+=_(TELNET_ENTER);
		}

		str+=wxT("iptables -t nat -A routercmd  -j DROP"); str+=_(TELNET_ENTER);
	}
	else
	{
		//process black list
		for(size_t i=0;i<lstBlack.list.size();i++)
		{
			if ( bLog )
			{
				str+=wxT("iptables -t nat -A routercmd -m mac --mac-source ");
				str+=wxString::FromAscii(lstBlack.list[i].strMAC.c_str());
				str+=wxT(" -j LOG --log-prefix \"MAC blocked\"");
				str+=wxT(TELNET_ENTER);
			}

			str+=wxT("iptables -t nat -A routercmd -m mac --mac-source ");
			str+=wxString::FromAscii(lstBlack.list[i].strMAC.c_str());
			str+=wxT(" -j DROP");
			str+=wxT(TELNET_ENTER);
		}

		str+=wxT("iptables -t nat -A routercmd -j RETURN");
		str+=wxT(TELNET_ENTER);
	}

	str.Replace(wxT("routercmd"), m_strMACBlockChainName); 

	return str;
}


wxString CScriptGenerator::GenerateMACBlockScriptXML(const MACBLOCK& lstWhite, const MACBLOCK& lstBlack, bool bLog)
{
	CMarkupSTL	doc;
	doc.AddElem( XML_SCRIPT_EL );
	doc.AddAttrib( GLOBAL_TYPE_ATR,		SHELL_MAC_FILTER  );
	doc.AddAttrib( GLOBAL_LOG_ATR,		(int)bLog  );

	for(size_t i=0;i<lstWhite.list.size();i++)
	{
		doc.AddChildElem( GLOBAL_ENTRY_EL );
		doc.AddChildAttrib(GLOBAL_MAC_ATTR, lstWhite.list[i].strMAC.c_str());

		if ( lstWhite.list[i].bEnableSched &&
			 lstWhite.list[i].IsBlockedByScheduler() )
		{
			//DROP
			doc.AddChildAttrib(GLOBAL_ACTION_ATTR, GLOBAL_DISABLE_VAL);
		}
		else
		{
			doc.AddChildAttrib(GLOBAL_ACTION_ATTR, GLOBAL_ENABLE_VAL);
		}
	}

	if ( lstWhite.blockOthers )
	{
		doc.AddChildElem( GLOBAL_ENTRY_EL );
		doc.AddChildAttrib(GLOBAL_MAC_ATTR, GLOBAL_ANY_VAL);
		doc.AddChildAttrib(GLOBAL_ACTION_ATTR, GLOBAL_DISABLE_VAL);
	}
	else
	{
		//process black list
		for(size_t i=0;i<lstBlack.list.size();i++)
		{
			doc.AddChildElem( GLOBAL_ENTRY_EL );
			doc.AddChildAttrib(GLOBAL_MAC_ATTR, lstBlack.list[i].strMAC.c_str());
			doc.AddChildAttrib(GLOBAL_ACTION_ATTR, GLOBAL_DISABLE_VAL);
		}
	}

	return wxString::FromAscii( doc.GetDoc().c_str() );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

wxString CScriptGenerator::GenerateAdvancedOptScript( const STRINGPAIRLIST& lst )
{
	wxString str, strFile;

	CMarkupSTL	doc;
	doc.AddElem( XML_SCRIPT_EL );
	doc.AddAttrib( GLOBAL_TYPE_ATR, SHELL_TCPNAT_SETTNGS );

	for ( STRINGPAIRLIST::const_iterator iCur=lst.begin();iCur!=lst.end();iCur++ )
	{
		const STRINGPAIR& pair = *iCur;
		strFile = GetPathFromName(pair.strLabel);
		strFile+=pair.strLabel;

		doc.AddChildElem(GLOBAL_ENTRY_EL);
		doc.AddChildAttrib(GLOBAL_NAME_ATR,  strFile.ToAscii() );
		doc.AddChildAttrib(GLOBAL_VALUE_ATR, pair.strVal.ToAscii() );

		str+=wxT("echo \"");
		str+=pair.strVal;
		str+=wxT("\">");
		str+= strFile;
		str+=_(TELNET_ENTER);
	}

	return m_bXML?wxString::FromAscii( doc.GetDoc().c_str() ): str;
}

wxString CScriptGenerator::GetPathFromName(const wxString& strName)
{
	if ( 0==strName.Find(wxT("ip_conntrack")) )
	{
		return DEF_NETFILTER_PATH;
	}

	if ( 0==strName.Find(wxT("tcp_")) )
	{
		return DEF_IP_PATH;
	}

	if ( 0==strName.Find(wxT("icmp_")) )
	{
		return DEF_IP_PATH;
	}

	assert(0);

	return DEF_IP_PATH;
}

wxString CScriptGenerator::GenerateProcKillScript(const TEXTLIST& lst)
{
	wxString str;

	assert( !lst.empty() );

	CMarkupSTL	doc;
	doc.AddElem( XML_SCRIPT_EL );
	doc.AddAttrib( GLOBAL_TYPE_ATR, SHELL_PROCKILL );

	for(size_t i=0;i<lst.size();i++)
	{
		doc.AddChildElem(GLOBAL_ENTRY_EL);
		doc.AddChildAttrib(GLOBAL_NAME_ATR,  lst[i].c_str() );

		str+=wxT("killall -9 ");
		str+=wxString::FromAscii( lst[i].c_str() );
		str+=wxT(TELNET_ENTER);
	}

	return m_bXML?wxString::FromAscii( doc.GetDoc().c_str() ): str;
}


wxString CScriptGenerator::GetProtoString( cPortFrorwardProto proto )
{
	wxString str;

	switch ( proto )
	{
		case cPortFrorwardProtoAny:
			assert(0);
		break;

		case cPortFrorwardProtoTCPIP:
			str=wxT(" -p tcp");
		break;

		case cPortFrorwardProtoUDP:
			str=wxT(" -p udp");
		break;
	}

	return str;
}

wxString CScriptGenerator::BuildPFRule(const PORTFORWARDRULE& rule, cPortFrorwardProto proto)
{
	wxString str;

	str+=wxT("iptables -A routercmdpf");
	str+=GetProtoString( proto );
	str+=wxT(" -d ");
	str+=rule.strDst;
	str+=wxT(" --dport ");
	str+=wxString::Format(wxT("%u:%u"), rule.wPortFrom, rule.wPortFromTo );
	str+=wxT(" -j ACCEPT");
	str+=wxT(TELNET_ENTER);

	str+=wxT("iptables -t nat -A routercmdnat");
	str+=GetProtoString( proto );
	str+=wxT(" --dport ");
	str+=wxString::Format(wxT("%u:%u"), rule.wPortFrom, rule.wPortFromTo );
	str+=wxT(" -j DNAT");
	str+=wxT(" --to ");
	str+=wxString::Format(wxT("%s:%u"),rule.strDst.c_str(), rule.wPortTo );;
	str+=wxT(TELNET_ENTER);

	return str; 
}

wxString CScriptGenerator::GeneratePortForwardScript(const PORTFORWARDLIST& lst )
{
	wxString str;

	if (m_bXML)
	{
		return GeneratePortForwardScriptXML(lst);
	}

	str+=wxT("iptables -t nat -N routercmdnat"); str+=wxT(TELNET_ENTER); //add new table
	str+=wxT("iptables -N routercmdpf"); str+=wxT(TELNET_ENTER); //add new table

	//Clean up chain
	str+=wxT("iptables -t nat -F routercmdnat"); str+=wxT(TELNET_ENTER); //clear table
	str+=wxT("iptables -F routercmdpf"); str+=wxT(TELNET_ENTER); //clear table


	//Clean old -j
	str+=wxT("iptables -t nat -D PREROUTING -j routercmdnat");str+=wxT(TELNET_ENTER);
	str+=wxT("iptables -D FORWARD -j routercmdpf");str+=wxT(TELNET_ENTER);
	
	//must be here
	str+=wxT("iptables -t nat -I PREROUTING 1 -j routercmdnat");str+=wxT(TELNET_ENTER);
	str+=wxT("iptables -I FORWARD 1 -j routercmdpf");str+=wxT(TELNET_ENTER);
	
	for(size_t i=0;i<lst.size();i++)
	{
		const PORTFORWARDRULE& rule = lst[i];
		if ( !rule.bEnabled ) continue;

		if ( rule.proto == cPortFrorwardProtoAny)
		{
			str+=BuildPFRule(rule, cPortFrorwardProtoTCPIP);
			str+=BuildPFRule(rule, cPortFrorwardProtoUDP);
		}
		else
			str+=BuildPFRule(rule, rule.proto);
	}

	//don't think we need this but...
	str+=wxT("iptables -t nat -A routercmdnat -j RETURN");
	str+=wxT(TELNET_ENTER);

	str+=wxT("iptables -A routercmdpf -j RETURN");
	str+=wxT(TELNET_ENTER);

	str.Replace(wxT("routercmdnat"), m_strNatChainName);
	str.Replace(wxT("routercmdpf"),  m_strPortForwardChainName);

	return str;
}

wxString CScriptGenerator::GeneratePortForwardScriptXML(const PORTFORWARDLIST& lst )
{
	wxString str;

	CMarkupSTL	doc;
	doc.AddElem( XML_SCRIPT_EL );
	doc.AddAttrib( GLOBAL_TYPE_ATR, SHELL_PORT_FORWARD );

	for(size_t i=0;i<lst.size();i++)
	{
		const PORTFORWARDRULE& rule = lst[i];
		if ( !rule.bEnabled ) continue;

		doc.AddChildElem(GLOBAL_ENTRY_EL);
		doc.AddChildAttrib(GLOBAL_NAME_ATR,  rule.strLabel.ToAscii() );
		doc.AddChildAttrib(GLOBAL_TYPE_ATR,  rule.proto );
		doc.AddChildAttrib(SCRIPT_PORT_FROM_ATTR, rule.wPortFrom);
		doc.AddChildAttrib(SCRIPT_PORT_FROM_TO_ATTR, rule.wPortFromTo);
		doc.AddChildAttrib(SCRIPT_DST_ATTR, rule.strDst);
		doc.AddChildAttrib(SCRIPT_DST_PORT_ATTR, rule.wPortTo);
	}

	return wxString::FromAscii( doc.GetDoc().c_str() );
}


wxString CScriptGenerator::GenerateStartUpScript()
{
	wxString str;


	return str;
}


////////////////////////////////////////////////////////////////
/*
Linux system and kernel logging utility.
Note that this version of syslogd ignores /etc/syslog.conf.

Options:
        -m MIN          Minutes between MARK lines (default=20, 0=off)
        -n              Run as a foreground process
        -O FILE         Use an alternate log file (default=/var/log/messages)
        -S              Make logging output smaller.
        -R HOST[:PORT]  Log to IP or hostname on PORT (default PORT=514/UDP)
        -L              Log locally and via network logging (default is network
only)
        -C [size(KiB)]  Log to a circular buffer (read the buffer using logread)
        -l [1-7]        local log level
        -r [1-7]        remote log level
*/
wxString CScriptGenerator::GenerateSyslogConfigScript( const wxString& strIP, long lPort, long lLogLevel )
{
	wxString str;

	CMarkupSTL	doc;
	doc.AddElem( XML_SCRIPT_EL );
	doc.AddAttrib( GLOBAL_TYPE_ATR, SHELL_SYSLOGCONFIG );
	doc.AddAttrib(GLOBAL_IP_ATTR, strIP.ToAscii() );
	doc.AddAttrib(GLOBAL_PORT_ATTR, lPort );
	doc.AddAttrib(GLOBAL_LEVEL_ATTR, lLogLevel );

	str+=wxT("killall -9 syslogd");
	str+=wxT(TELNET_ENTER);

	str+=wxString::Format(wxT("syslogd -R %s:%d -r %d -l %d&"), strIP.c_str(), lPort, lLogLevel, lLogLevel);
	str+=wxT(TELNET_ENTER);

	return m_bXML?wxString::FromAscii( doc.GetDoc().c_str() ): str;
}

wxString CScriptGenerator::GenerateModUnloadScript(const TEXTLIST& lst)
{
	wxString str;

	CMarkupSTL	doc;
	doc.AddElem( XML_SCRIPT_EL );
	doc.AddAttrib( GLOBAL_TYPE_ATR, SHELL_KERNELMOD );

	assert( !lst.empty() );

	str = wxT("rmmod -a");
	str+=wxT(TELNET_ENTER);

	for(size_t i=0;i<lst.size();i++)
	{
		doc.AddChildElem( GLOBAL_ENTRY_EL );
		doc.AddChildAttrib( GLOBAL_NAME_ATR, lst[i].c_str() );

		str+=wxT("rmmod ");
		str+=wxString::FromAscii( lst[i].c_str() );
		str+=wxT(TELNET_ENTER);
	}

	return m_bXML?wxString::FromAscii( doc.GetDoc().c_str() ): str;
}
